﻿using System;
using System.Collections.Generic;
using Microsoft.Popfly.GameCreator.GameEngine.Events;

namespace Microsoft.Popfly.GameCreator.GameEngine.Behaviors
{
    public class DisappearBehavior : IBehavior
    {
        private enum DisappearType { ACTOR, ACTORINSTANCE, SELF, SENDER };
        private Data.EffectInfo m_effect;
        private string m_actorOrInstance;
        private BehaviorOwner m_owner;
        private DisappearType m_disappearType;
        private string m_explosionActorName;
        private Game m_game;

        public DisappearBehavior(Game game, BehaviorOwner owner, Data.BehaviorInfo behaviorInfo)
        {
            if (behaviorInfo.GetType() != typeof(Data.DisappearBehaviorInfo))
            {
                throw new ArgumentException(
                    Utils.ErrorMessagePrefix +
                    "DisappearBehavior only takes behaviorInfo of type DisappearBehaviorInfo.  behaviorInfo was of type '" +
                    behaviorInfo.GetType().ToString() + "'."
                    );
            }
            m_game = game;
            m_owner = owner;

            Data.DisappearBehaviorInfo disappearInfo = (Data.DisappearBehaviorInfo)behaviorInfo;

            if (disappearInfo.ItemReference == null)
            {
                m_disappearType = DisappearType.SELF;
            }
            else
            {
                switch (disappearInfo.ItemReference.Type)
                {
                    case "actor" :
                        {
                            m_disappearType = DisappearType.ACTOR;

                            if (disappearInfo.ItemReference.Includes.Count != 1)
                            {
                                throw new ArgumentException(
                                    "Disappear Actor behaviors only support a single actor name.  Number of actors specified: '" +
                                    disappearInfo.ItemReference.Includes.Count + "'.");
                            }
                            Dictionary<String, Data.IncludeObjectInfo>.Enumerator e = disappearInfo.ItemReference.Includes.GetEnumerator();
                            e.MoveNext();
                            
                            m_actorOrInstance = e.Current.Key;
                        }
                        break;
                    case "actorInstance" :
                        {
                            m_disappearType = DisappearType.ACTORINSTANCE;

                            if (disappearInfo.ItemReference.Includes.Count != 1)
                            {
                                throw new ArgumentException(
                                    "Disappear Actor Instance behaviors only support a single actor instance.  Number of actor instances specified: '" +
                                    disappearInfo.ItemReference.Includes.Count + "'.");
                            }
                            Dictionary<String, Data.IncludeObjectInfo>.Enumerator e = disappearInfo.ItemReference.Includes.GetEnumerator();
                            e.MoveNext();
                            
                            m_actorOrInstance = e.Current.Key;
                        }
                        break;
                    case "eventSender" :
                        m_disappearType = DisappearType.SENDER;
                        break;
                    case null :
                    case "game" :
                    case "self" :
                        m_disappearType = DisappearType.SELF;
                        if (!owner.IsActor)
                        {
                            throw new ArgumentException(
                                Utils.ErrorMessagePrefix +
                                "If a disappear event specifies 'Self' as it's target, it must be an actor.  " +
                                "(Scenes are not valid targets for Disappear behaviors.)"
                                );
                        }
                        break;
                    default :
                        throw new NotImplementedException(
                            Utils.ErrorMessagePrefix + 
                            "DisappearBehaviors don't know how to handle a disappear type of '" + 
                            disappearInfo.ItemReference.Type + 
                            "'."
                            );
                }
            }

            if (disappearInfo.ActorReference != null)
            {
                m_explosionActorName = disappearInfo.ActorReference.Name;
            }
            m_effect = disappearInfo.Effect;
        }

        public void Invoke(BehaviorOwner sender, PGCEventArgs args)
        {
            List<Actor> targets = new List<Actor>();

            switch (m_disappearType)
            {
                case DisappearType.ACTOR :
                    targets.AddRange(m_owner.Scene.GetActors(m_actorOrInstance));
                    if (targets.Count == 0)
                    {
                        Console.IssueWarning(
                            "Could not remove actors of type '" +
                            m_actorOrInstance +
                            "' because no actors of that type currently exist in scene '" + m_owner.Scene.Name + "'."
                            );
                    }
                    break;
                case DisappearType.ACTORINSTANCE :
                    Actor actorTarget = m_owner.Scene.GetActor(m_actorOrInstance);
                    if (actorTarget == null)
                    {
                        Console.IssueWarning(
                            "Could not remove actor instance '" +
                            m_actorOrInstance +
                            "' because it does not currently exist in scene '" + m_owner.Scene.Name + "'."
                            );
                    }
                    else
                    {
                        targets.Add(actorTarget);
                    }
                    break;
                case DisappearType.SELF :
                    System.Diagnostics.Debug.Assert(m_owner.IsActor);
                    targets.Add((Actor) m_owner);
                    break;
                case DisappearType.SENDER :
                    if (sender != null && sender.IsActor)
                    {
                        targets.Add((Actor) sender);
                    }

                    // Special handling for collision events to make sure
                    // both colliding actors are disappeared
                    if (args.GetType() == typeof(CollisionEventArgs))
                    {
                        targets.Add(((CollisionEventArgs)args).Actor2);
                    }
                    break;
                default :
                    throw new NotImplementedException(
                        Utils.ErrorMessagePrefix +
                        "DisappearBehaviors don't know how to invoke on disappear types of '" +
                        m_disappearType + "'.");
                    
            }

            foreach (Actor target in targets)
            {
                // if there was an actor based effect, spawn it now
                if (m_explosionActorName != null)
                {
                    m_owner.Scene.AddActor(target, m_explosionActorName);
                }
                target.Remove(m_effect);
            }
        }
    }
}